// This file is part of OpenMeca, an easy software to do mechanical simulation.
//
// Author(s)    :  - Damien ANDRE  <openmeca@gmail.com>
//
// Copyright (C) 2012 Damien ANDRE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include <cassert>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include "OpenMeca/Core/System.hpp"
#include "OpenMeca/Core/AutomaticItem.hpp"
#include "OpenMeca/Core/Drawable.hpp"
#include "OpenMeca/Item/Body.hpp"
#include "OpenMeca/Item/LinkBase.hpp"
#include "OpenMeca/Item/PartLink.hpp"
#include "OpenMeca/Item/PartUserPoint.hpp"
#include "OpenMeca/Gui/MainWindow.hpp"


namespace OpenMeca
{

  namespace Core
  {

    //************** Static member and attributes *************************
    System* System::activeSystem_ = 0;

    bool System::deleted_ = false;
  
    bool
    System::IsDeleted()
    {
      return deleted_;
    }


    System& 
    System::Get()
    {
      assert(IsDeleted() == false);
      if (activeSystem_==0)
    	activeSystem_ = new System();
      return *activeSystem_;
    }

    void
    System::Quit()
    {
      delete &System::Get();
      System::deleted_ = true;
    }


  
    //************** Constructors and Destructors *************************
    System::System()
      :settings_(),
       historic_(*this)
    {
    }

    System::~System()
    {
      GetSetOf<UserRootItem>().DeleteAllItem();
    }
    
    bool
    System::IsEdited()
    {
      return historic_.IsSystemEdited();
    }

    void
    System::Update()
    {
      {
	Core::SetOf<UserRootItem>::it it;
	for (it = GetSetOf<UserRootItem>().Begin() ; it != GetSetOf<UserRootItem>().End(); it++ )
	  {	  
	    (*it)->Update();
	  }
      }

      {
	Core::SetOf<AutomaticItem>::it it;
	for (it = GetSetOf<AutomaticItem>().Begin() ; it != GetSetOf<AutomaticItem>().End(); it++ )
	  {	  
	    (*it)->Update();
	  }
      }
      settings_.Update();
      Gui::MainWindow::Get().GetViewer().updateGL(); 
    }

    HistoricManager& 
    System::GetHistoric()
    {
      return historic_;
    }
    

    const HistoricManager& 
    System::GetHistoric() const
    {
      return historic_;
    }
    


    void
    System::Save(std::string& str)
    {
      std::stringstream ss;
      boost::archive::text_oarchive oa(ss);
      const SetOf<UserItem>& userItems =  GetSetOf<UserItem>();
      oa << userItems;
      oa << settings_;
      str = ss.str();
    }
    

    void
    System::Load(std::string& str)
    {      
      Reset();
      std::stringstream ss;
      ss << str;
      boost::archive::text_iarchive oa(ss);
      oa >> GetSetOf<UserItem>();
      oa >> settings_;
    }


    void
    System::SaveFile(std::string& str)
    {
      std::ofstream ofs(str.c_str());
      boost::archive::text_oarchive oa(ofs);
      const SetOf<UserItem>& userItems =  GetSetOf<UserItem>();
      oa << userItems;
      oa << settings_;
    }
    
    void
    System::LoadFile(std::string& str)
    {    
      Reset();
      std::ifstream ifs(str.c_str());
      boost::archive::text_iarchive ia(ifs);
      ia >> GetSetOf<UserItem>();
      ia >> settings_;
      historic_.Reset();
      Update();
    }

    void
    System::Reset()
    {
      Singleton<SelectionManager>::Get().SetNoItemSelected();
      GetSetOf<UserRootItem>().ClearAndDelete();
    }

    void
    System::Draw() const
    {
      SetOf<Drawable>::const_it it;
      for (it=GetSetOf<Drawable>().Begin() ; it != GetSetOf<Drawable>().End(); it++ )
	{
	  (*it)->Draw();
	}
    }

    void
    System::DrawWithNames() const
    {
      SetOf<Drawable>::const_it it;
      for (it=GetSetOf<Drawable>().Begin() ; it != GetSetOf<Drawable>().End(); it++ )
	{
	  (*it)->Draw(true);
	}
    }

    


    std::string
    System::GetAutomaticName(const std::string& childTypeStr) const
    {
      int maxNumber = 0;
      SetOf<UserItem>::const_it it;
      for (it=GetSetOf<UserItem>().Begin() ; it != GetSetOf<UserItem>().End(); it++ )
	{
	  std::size_t size = (*it)->GetName().find(childTypeStr);
	  if (size != std::string::npos)
	    {
	      if ((*it)->GetName().size() >= childTypeStr.size() + 2)
		{
		  std::string copy = (*it)->GetName();
		  copy.erase(0, childTypeStr.size() + 1);
		  int num = atoi (copy.c_str());
		  if (num > maxNumber)
		    maxNumber = num;
		}
	    }
	}
      maxNumber++;
      return childTypeStr + '_' + boost::lexical_cast<std::string>(maxNumber);
    }

    SystemSettings& 
    System::GetSettings()
    {
      return settings_;
    }
    
    const SystemSettings& 
    System::GetSettings() const
    {
      return settings_;
    }


  }
}





